package com.yc.happ.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DBHelper {

		static{
			//静态块，当程序Load上来，静态块便一块加载上来了，为了只需加载一次的驱动，在整个程序便有效
			//避免多次加载，以提高程序的效率
			try {
				//根据类加载器，获得驱动名 加载驱动
				Class.forName(MyProperties.getInstance("").getProperty("driverClassName"));
				getConnection();
			} catch (ClassNotFoundException e) {
				 
			}
			
		}
		public static Connection getConnection() {
			Connection conn = null;
			//通过驱动管理器，创建数据库的连接
			//根据类加载器获得创建连接时的各个参数  创建连接
			try {
				conn=DriverManager.getConnection(MyProperties.getInstance("").getProperty("url"),MyProperties.getInstance("").getProperty("username"),MyProperties.getInstance("").getProperty("password"));
			} catch (SQLException e) {
				e.printStackTrace();
			}
			return conn;	
		}
		/*
		 * 设置参数
		 * 
		 * **/
		//执行句柄中设置参数
			public static void setParams(PreparedStatement preparedStatement,List<Object> params){
				//容错机制
				if(preparedStatement==null||params==null||params.size()<=0){
					return;//直接跳出此方法  继续往下执行
				} 
				//设置参数  取出链表中的每个元素作为执行句柄的参数
				
				//为何要一起捕获呢  原因：因为只要有一个参数设置出现异常那么就应该立即停止设置参数  因为执行语句已经不对了
				try {
					for(int i=0;i<params.size();i++){
						//取出链表中的元素  下标是从0开始
						Object param=params.get(i);
						//设置参数  全都当成字符串来设置参数    preparedStatement的下标是从1开始的
						//所有的参数都是当成字符串来设置，所以是setString
						preparedStatement.setString(i+1, param.toString());
					}
				} catch (SQLException e) {
					 
					//把受检异常转换成非受检异常  往上一层抛出同意  在控制台打印出来
					throw new RuntimeException(e);
				}		
			}
		/*
		 *关闭数据库的连接
		 *
		 */
		
		public static void  closeAll(ResultSet rs,PreparedStatement  pstmt ,Connection con) {
			try {
				
				//关闭连接的时候，判断是否为空，避免出现空指针的情况的出现
				if(rs!=null){
					rs.close();
				}
			} catch (SQLException e) {
				 
 
				throw new RuntimeException(e);
			}
			try {
				if(pstmt!=null){
					pstmt.close();
				}
			} catch (SQLException e) {
				 
				 
				throw new RuntimeException(e);
			}
			try {
				if(con!=null){
					con.close();
				}
			} catch (SQLException e) {
				 
				 
				throw new RuntimeException(e);
			}
		}
		/*单sql语句的更新
		 * 更新语句的
		 * 
		 * **/
		public static int doUpdate(String sql ,List<Object>params){
			int r=0;
			PreparedStatement stmt=null;
			Connection con=null;
			try {
				con=getConnection();
				stmt=con.prepareStatement(sql);
				setParams(stmt, params);
				r=stmt.executeUpdate();
			} catch (SQLException e) {
				 e.printStackTrace();
				 throw new RuntimeException(e); 
			}finally {
				closeAll(null,stmt , con);
			}
			return r;
		}
		/*
		 * 执行多条语句的更新
		 * 
		 * **/
		//处理多个sql语句的方法
			public static void doUpdate(List<String> sqls,List<List<Object>> params)  {
				Connection connection=null;
				PreparedStatement preparedStatement=null;
				try {
					//加载驱动     写在静态块里   编译时就将驱动加载到java程序当中   连接数据库
					connection=getConnection();
					//关闭隐式事务  变成显示事务  实行多条语句的更新
					//保证执行多条数据后再进行数据的commit的操作
					connection.setAutoCommit(false);
					//创建执行句柄并且循环执行
					//比单条语句的更新的多做的一步操作就是将sql语句从sqls链表中取出来 将param从parans链表中取出
					//再调用setParam（）方法来设置参数
					for(int i=0;i<sqls.size();i++){
						String sql=sqls.get(i);
						preparedStatement=connection.prepareStatement(sql);
						
						//获取与当前sql语句对应的参数
						List<Object> ps=params.get(i);
						//设置执行句柄的参数
					   setParams(preparedStatement, ps);
						  
						//为什么执行到这里就停住了
						preparedStatement.executeUpdate();
						
					}
					 
					//当所有的数据库操作都已经操作完毕  在统一提交事务 保证数据得更改是同步
					connection.commit();		
				} catch (SQLException e) {
					
						//当提交事务时   出现异常    回滚事务  保证数据的安全的性
						try {
							connection.rollback();
						} catch (SQLException e1) {
							e1.printStackTrace();
						}
				
					//异常中finally是一定会执行的  一般用来关闭资源的以免造成资源的浪费
				}finally{
					//恢复隐式事务
					try {
						//回复现场 
						connection.setAutoCommit(true);
					} catch (SQLException e) {
						//e.printStackTrace();
						 
						throw new RuntimeException(e);
					}
					closeAll(null, preparedStatement, connection);
				}
				
			}		
			/*
			 * 支持聚合函数的查询
			 * 
			 * **/
			public static double doSelectFunt(String sql ,List<Object>params){
				double r=0.0;
				PreparedStatement pstmt=null;
				Connection con=null;
				con=getConnection();
				try {
					pstmt=con.prepareStatement(sql);
					setParams(pstmt, params);
					ResultSet rs=pstmt.executeQuery();
					if(rs.next()){
						
						r=rs.getDouble(1);
					}
				} catch (SQLException e) {
					e.printStackTrace();
					throw new RuntimeException(e);
				}
				return r;
			}
		 /*
		 * 查询语句
		 * 
		 * */
		public static List<Map<String, String>> doSelect(String sql ,List<Object>params){
			List<Map<String, String>>list=null;
			PreparedStatement pstmt=null;
			Connection con=null;
			
			try {
				con=getConnection();
				pstmt=con.prepareStatement(sql);
				setParams(pstmt, params);
				ResultSet rs=pstmt.executeQuery();
				//取出所有的列名，用于map中的键
				ResultSetMetaData rsmd=rs.getMetaData();//ResultSetMetaData
				List<String>columnNames=new  ArrayList<String>();
				
				for(int i=0;i<rsmd.getColumnCount();i++){
					String cname=rsmd.getColumnName(i+1);//获得列名
					columnNames.add(cname);//将列名存放在list中作为Map的键
				}
				list=new ArrayList<Map<String , String>>();//重新定义一个list，存储Map类型
				while(rs.next()){
					//一行就是一个Map
					Map<String, String>object=new HashMap<String,String>();//定义一个Map类型
					
					for(String cname:columnNames){//list是个有序的列表
						
						String value=rs.getString(cname);//根据列名来取值
						object.put(cname, value);//将键值对存放在Map中
					}
					list.add(object);//将Map存放到List中
					
				}
			} catch (SQLException e) {
			 
				e.printStackTrace();
				throw new RuntimeException(e);
			}finally {
				closeAll(null, pstmt, con);
			}
			return list;
		}
		/**
		 * @param t
		 *            插入数据的对象
		 * @return 受影响的行数
		 */
		public static <T> int save(T t) {
			
			Class<?> c = t.getClass();
			List<Object> params = new ArrayList<Object>();
			String tableName = c.getName().substring(c.getName().lastIndexOf(".") + 1);
			Field[] fs = c.getDeclaredFields(); // 取到指定类对象的类的属性
			System.out.println("进入save");
			String sql = "insert into " + tableName + "(";
			String sqlValue = " values(";
			try {
				for (Field f : fs) {
					String fn = f.getName(); // 取到属性名
					String mn = "get" + fn.substring(0, 1).toUpperCase()
							+ fn.substring(1); // 对应属性的set方法名
					Method m =null;
					try{
						m= c.getDeclaredMethod(mn);
					}catch(Exception e){
						continue;
					}
					Object obj = m.invoke(t);
					if (obj != null) {
						sql += fn + ",";
						sqlValue += "?,";
						params.add(obj);
						System.out.println(fn + " == " + obj);
					}
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
			sql = sql.substring(0, sql.length() - 1) + ")";
			sqlValue = sqlValue.substring(0, sqlValue.length() - 1) + ")";
			sql = sql + sqlValue;
			System.out.println(sql);
			return doUpdate(sql, params);
		}
		/**
		 * 
		 * @param t 插入数据的对象
		 * @param seqName  序列名
		 * @param idColumn 主键名
		 * @return 受影响的行数
		 */
		public static <T> int save(T t, String seqName, String idColumn) {
			SqlAndParams sqlAndParams=getSaveParams(t, seqName, idColumn);
			return doUpdate(sqlAndParams.sql, sqlAndParams.params);
		}
		/*public static <T> int save(T t, String seqName, String idColumn) {
			Class<?> c = t.getClass();
			List<Object> params = new ArrayList<Object>();
			String tableName = c.getName().substring(c.getName().lastIndexOf(".") + 1);
			Field[] fs = c.getDeclaredFields(); // 取到指定类对象的类的属性
			String sql = "insert into " + tableName + "(";
			String sqlValue = " values(";
			try {
				for (Field f : fs) {
					String fn = f.getName(); // 取到属性名
					String mn = "get" + fn.substring(0, 1).toUpperCase()
							+ fn.substring(1); // 对应属性的set方法名
					Method m =null;
					try{
						m=c.getDeclaredMethod(mn);
					}catch(Exception e){
						continue;
					}
					Object obj = m.invoke(t);
					if (obj != null) {
						sql += fn + ",";
						sqlValue += "?,";
						params.add(obj);
					}
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
			if (seqName != null && idColumn != null){
				sql = sql + idColumn + ")";
				sqlValue = sqlValue+ seqName + ".nextval)";
			}else{
				sql = sql.substring(0, sql.length() - 1) + ")";
				sqlValue = sqlValue.substring(0, sqlValue.length() - 1) + ")";
			}
			sql = sql + sqlValue;
			System.out.println(sql);
			return doUpdate(sql, params);
		}*/
		/**
		 * 
		 * @param t 插入数据的对象
		 * @param seqName  序列名
		 * @param idColumn 主键名
		 * @return 受影响的行数
		 */
		public static <T> SqlAndParams getSaveParams(T t, String seqName, String idColumn) {
			Class<?> c = t.getClass();
			List<Object> params = new ArrayList<Object>();
			String tableName = c.getName().substring(c.getName().lastIndexOf(".") + 1);
			Field[] fs = c.getDeclaredFields(); // 取到指定类对象的类的属性
			String sql = "insert into " + tableName + "(";
			String sqlValue = " values(";
			try {
				for (Field f : fs) {
					String fn = f.getName(); // 取到属性名
					String mn = "get" + fn.substring(0, 1).toUpperCase()
							+ fn.substring(1); // 对应属性的set方法名
					Method m =null;
					try{
						m=c.getDeclaredMethod(mn);
					}catch(Exception e){
						continue;
					}
					Object obj = m.invoke(t);
					if (obj != null) {
						sql += fn + ",";
						sqlValue += "?,";
						params.add(obj);
					}
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
			if (seqName != null && idColumn != null){
				sql = sql + idColumn + ")";
				sqlValue = sqlValue+ seqName + ".nextval)";
			}else{
				sql = sql.substring(0, sql.length() - 1) + ")";
				sqlValue = sqlValue.substring(0, sqlValue.length() - 1) + ")";
			}
			sql = sql + sqlValue;
			System.out.println(sql);
			SqlAndParams sqlandparams=new DBHelper().new SqlAndParams();
			sqlandparams.sql=sql;
			sqlandparams.params=params;
			return sqlandparams;
		}
		
		public static <T> T query(T t) {
			Class<?> c = t.getClass();
			List<Object> params = new ArrayList<Object>();
			String tableName = c.getName().substring(c.getName().lastIndexOf(".") + 1);
			Field[] fs = c.getDeclaredFields(); // 取到指定类对象的类的属性
			String sql = "select * from " + tableName ;
			String sqlValue = "";
			try {
				for (Field f : fs) {
					String fn = f.getName(); // 取到属性名
					String mn = "get" + fn.substring(0, 1).toUpperCase()
							+ fn.substring(1); // 对应属性的set方法名
					Method m =null; 
					try{
						m=c.getDeclaredMethod(mn);
					}catch(Exception e){
						continue;
					}
					Object obj = m.invoke(t);
					if (obj != null) {
						sqlValue += fn + "=? and ";
						params.add(obj);
					}
				}
			} catch (Exception e) {
				t = null;
				throw new RuntimeException(e);
			}
			sqlValue = sqlValue.length() == 0 ? "" : ( " where " + sqlValue.substring(0, sqlValue.length() - 5)) ;
			sql = sql + sqlValue;
			System.out.println(sql);
			 try {
				 Map<String, String> results = doSelect(sql, params).get(0);
					for (Field f : fs) {
						String fn = f.getName(); // 取到属性名
						String mn = "set" + fn.substring(0, 1).toUpperCase()
								+ fn.substring(1); // 对应属性的set方法名
						Method m =null;
						try{
							m=c.getDeclaredMethod(mn, f.getType());
						}catch(Exception e){
							continue;
						}
						if(f.getType().getName().equals(Integer.class.getName())){
							 m.invoke(t, Integer.valueOf(results.get(fn.toUpperCase())));
						}
						else if(f.getType().getName().equals(Double.class.getName())){
							 m.invoke(t, Double.valueOf(results.get(fn.toUpperCase())));
						}else{
							m.invoke(t, results.get(fn.toUpperCase()));
						}
					}
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			return t;
		}
		public static <T> List<T> queryList(T t) {
			Class<?> c = t.getClass();
			List<T> ts = null;
			List<Object> params = new ArrayList<Object>();
			String tableName = c.getName().substring(c.getName().lastIndexOf(".") + 1);
			Field[] fs = c.getDeclaredFields(); // 取到指定类对象的类的属性
			String sql = "select * from " + tableName ;
			String sqlValue = "";
			try {
				for (Field f : fs) {
					String fn = f.getName(); // 取到属性名
					
					String mn = "get" + fn.substring(0, 1).toUpperCase()
							+ fn.substring(1); // 对应属性的set方法名
					Method m =null;
					try{
						m=c.getDeclaredMethod(mn);
					}catch(Exception e){
						continue;
					}
					Object obj = m.invoke(t);
					if (obj != null) {
						sqlValue += fn + "=? and ";
						params.add(obj);
					}
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
			sqlValue = sqlValue.length() == 0 ? "" : ( " where " + sqlValue.substring(0, sqlValue.length() - 5)) ;
			sql = sql + sqlValue;
			System.out.println(sql);
			 try {
				List<Map<String, String>> rs = doSelect(sql, params);
				ts = new ArrayList<T>();
				for (Map<String, String> results : rs) {
					T temp = (T) c.newInstance();
					for (Field f : fs) {
						String fn = f.getName(); // 取到属性名
						String mn = "set" + fn.substring(0, 1).toUpperCase()
								+ fn.substring(1); // 对应属性的set方法名
						Method m =null;
						try{
							m=c.getDeclaredMethod(mn, f.getType());
						}catch(Exception e){
							continue;
						}
						if(f.getType().getName().equals(Integer.class.getName())){
							 m.invoke(temp, Integer.valueOf(results.get(fn.toUpperCase())));
						}
						else if(f.getType().getName().equals(Double.class.getName())){
							 m.invoke(temp, Double.valueOf(results.get(fn.toUpperCase())));
						}else{
							m.invoke(temp, results.get(fn.toUpperCase()));
						}
					}
					 ts.add(temp);
				}
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			return ts;
		}

		public static <T> boolean update(T t,String idColumn) {
			Class<?> c = t.getClass();
			List<Object> params = new ArrayList<Object>();
			String tableName = c.getName().substring(c.getName().lastIndexOf(".") + 1);
			Field[] fs = c.getDeclaredFields(); // 取到指定类对象的类的属性
			String sql = "update " + tableName + " set ";
			String whereSql=" where  "+idColumn;
			try {
				for (Field f : fs) {
					String fn = f.getName(); // 取到属性名
					String mn = "get" + fn.substring(0, 1).toUpperCase()
							+ fn.substring(1); // 对应属性的set方法名
					Method m =null;
					try{
						m=c.getDeclaredMethod(mn);
					}catch(Exception e){
						continue;
					}
					Object obj = m.invoke(t);
					if(fn.equals(idColumn)&&obj==null){
						throw new RuntimeException("没有找到当主键的字段值！！！");
					}
					if(fn.equals(idColumn)){
						whereSql+="="+obj;
					}
					if (obj != null) {
						sql += fn + "=?,";
						params.add(obj);
					}
				}

			sql = sql.substring(0,sql.length()-1);
			sql=sql+whereSql;
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
			//update Food set fid=4,fname='鱼香肉丝',price=30.0,nowprice=20.0,ftype='炒',description='好吃不油腻',status=1 where fid=4;
			/*SqlAndParams sqlAndParams=new DBHelper().new SqlAndParams();
			sqlAndParams.sql=sql;
			sqlAndParams.params=params;*/
			System.out.println(sql);
			return doUpdate(sql, params)>0;
		}
		//内部类 存储sql语句和 参数
		public class SqlAndParams{
			public String sql;
			public List<Object> params;
		}
	}